#include "LaserAutoCalibrator.h"

#include <fstream> 
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>

#define ESC 27
#define SPACE 32

void LaserAutoCalibrator :: mouseClickedLaser( int x, int y, char mouse ){
    if (mouse == 'l')  //заливаем похожие пиксели цветом color
    {
        printf("%d x %d\n", x, y);
        CvConnectedComp comp;

        //заливаем пиксели, похожие на выделенный, цветом color
        cvFloodFill( imgCopy, cvPoint(x,y), CV_RGB(255, 0, 0), 
                     cvScalarAll(0), // минимальная разность
                     cvScalarAll(15), // максимальная разность
                     &comp,
                     CV_FLOODFILL_FIXED_RANGE + 8,
                     0);

        // покажем площадь заливки
        printf("[filled area] %.2f\n", comp.area);

        //на imgCopy область с указкой залита цветом color - ищем её истинный цвет на calibrationImage
        int height    = imgCopy->height;
        int width     = imgCopy->width;

        //бежим по всем пикселям
        for(int i = 0; i < height; i++){
            uchar* ptr = (uchar*)(imgCopy->imageData + i * imgCopy->widthStep);
            uchar* ptrOrig = (uchar*)(calibrationImage->imageData + i * calibrationImage->widthStep);
            for(int j = 0; j < width; j++){
                if ((ptr[3*j] == color.val[0]) && (ptr[3*j+1] == color.val[1]) && (ptr[3*j+2] == color.val[2])) { //BGR
                    if (ptrOrig[3*j] <= Hmin){
                        Hmin = ptrOrig[3*j] - 1;
                    }
                    if (ptrOrig[3*j] >= Hmax){
                        Hmax = ptrOrig[3*j] + 1;
                    }
                    if (ptrOrig[3*j+1] <= Smin){
                        Smin = ptrOrig[3*j+1] - 1;
                    }
                    if (ptrOrig[3*j+1] >= Smax){
                        Smax = ptrOrig[3*j+1] + 1;
                    }
                    if (ptrOrig[3*j+2] <= Vmin){
                        Vmin = ptrOrig[3*j+2] - 1;
                    }
                    if (ptrOrig[3*j+2] >= Vmax){
                        Vmax = ptrOrig[3*j+2] + 1;
                    }
                }
            }
        }
    }
    if (mouse == 'r') //увеличиваем картинку в scaleFactor раз
    {
        printf("%d x %d\n", x, y);
        int scaleFactor = 3;

        IplImage* dst;
        // создание увеличенной картинки
        dst = cvCreateImage( cvSize(calibrationImage->width*scaleFactor, calibrationImage->height*scaleFactor),
                             calibrationImage->depth, calibrationImage->nChannels );
        cvResize(calibrationImage, dst, 0); // nearest-neigbor - интерполяция методом ближайшего соседа

        int x0 = scaleFactor*x-(calibrationImage->width/2);
        int y0 = scaleFactor*y-(calibrationImage->height/2);
        if (x0 < 0){
                x0 = 0;
        }
        if ((x0 + calibrationImage->width) > dst->width){
                x0 = dst->width - dst->width/scaleFactor;
        }
        if (y0 < 0){
                y0 = 0;
        }
        if ((y0 + calibrationImage->height) > dst->height){
                y0 = dst->height - dst->height/scaleFactor;
        }
        
        cvSetImageROI(dst, cvRect(x0, y0, calibrationImage->width, calibrationImage->height));

        // обнулим изображение
        cvZero(calibrationImage);
        // копируем изображение
        cvCopyImage(dst, calibrationImage);
        cvResetImageROI(calibrationImage);

        // освобождаем ресурсы
        cvReleaseImage(&dst);

        //увеличиваем запасную копию изображения
        IplImage* dstCopy;
        // создание увеличенной картинки
        dstCopy = cvCreateImage( cvSize(imgCopy->width*scaleFactor, imgCopy->height*scaleFactor),
                                 imgCopy->depth, imgCopy->nChannels );
        cvResize(imgCopy, dstCopy, 0); // nearest-neigbor - интерполяция методом ближайшего соседа

        dst = cvCreateImage( cvSize(calibrationImage->width*scaleFactor, calibrationImage->height*scaleFactor),
                             calibrationImage->depth, calibrationImage->nChannels );
        cvResize(calibrationImage, dst, 0); // nearest-neigbor - интерполяция методом ближайшего соседа

        int xCopy0 = scaleFactor*x-(imgCopy->width/2);
        int yCopy0 = scaleFactor*y-(imgCopy->height/2);
        if (xCopy0 < 0){
                xCopy0 = 0;
        }
        if ((xCopy0 + imgCopy->width) > dstCopy->width){
                xCopy0 = dstCopy->width - dstCopy->width/scaleFactor;
        }
        if (yCopy0 < 0){
                yCopy0 = 0;
        }
        if ((yCopy0 + imgCopy->height) > dstCopy->height){
                yCopy0 = dstCopy->height - dstCopy->height/scaleFactor;
        }
        
        cvSetImageROI(dstCopy, cvRect(xCopy0, yCopy0, imgCopy->width, imgCopy->height));

        // обнулим изображение
        cvZero(imgCopy);
        // копируем изображение
        cvCopyImage(dstCopy, imgCopy);
        cvResetImageROI(imgCopy);

        // освобождаем ресурсы
        cvReleaseImage(&dstCopy);

    }
}

// обработчик событий от мышки
void myMouseCallbackFill( int event, int x, int y, int flags, void* param ){
    LaserAutoCalibrator* c = (LaserAutoCalibrator*) param;
    switch( event ){
        case CV_EVENT_MOUSEMOVE: 
            break;

        case CV_EVENT_LBUTTONDOWN:
            c->mouseClickedLaser(x, y, 'l');
            break;

        case CV_EVENT_LBUTTONUP:
            break;

        case CV_EVENT_RBUTTONDOWN:
            c->mouseClickedLaser(x, y, 'r');
            break;

        case CV_EVENT_RBUTTONUP:
            break;
    }
}

void LaserAutoCalibrator :: getLaserAuto(){
    assert( calibrationImage != 0 );

    // покажем изображение
    //cvNamedWindow( "original",  CV_WINDOW_AUTOSIZE );
    cvNamedWindow( "getLaser",  CV_WINDOW_AUTOSIZE );
    cvMoveWindow("getLaser", 0, 0);


    imgCopy = cvCreateImage( cvGetSize(calibrationImage), IPL_DEPTH_8U, 3 );
    /* copy from calibrationImage to imgCopy */
    cvCopy( calibrationImage, imgCopy, NULL );
    cvCvtColor( imgCopy, calibrationImage, CV_BGR2HSV ); 

    original = cvCreateImage( cvGetSize(calibrationImage), IPL_DEPTH_8U, 3 );
    /* copy from calibrationImage to original */
    cvCopy( calibrationImage, original, NULL );

    Hmin = 256;
    Hmax = 0;
    Smin = 256;
    Smax = 0;
    Vmin = 256;
    Vmax = 0;

    //CV_RGB(255, 0, 0)
    color.val[0] = 0;
    color.val[1] = 0;
    color.val[2] = 255;

    // вешаем обработчик мышки
    cvSetMouseCallback( "getLaser", myMouseCallbackFill, this);

    while(true){
        // показываем картинку
        cvShowImage( "getLaser", imgCopy );
        //cvShowImage( "original", calibrationImage );

        char c = cvWaitKey(33);
        if (c == SPACE) { // если нажат SPACE отменяем всю заливку
            // обнулим изображение
            cvZero(imgCopy);
            cvCopy( calibrationImage, imgCopy, NULL );
            cvCvtColor( calibrationImage, imgCopy, CV_HSV2BGR ); 
        }
        if (c == ESC) { // если нажата ESC - выходим
            break;
        }
    }

    IplImage* h_plane = 0;
    IplImage* s_plane = 0;
    IplImage* v_plane = 0;
    // для хранения каналов HSV после преобразования
    IplImage* h_range = 0;
    IplImage* s_range = 0;
    IplImage* v_range = 0;
    // для хранения суммарной картинки
    IplImage* hsv_and = 0;
    h_plane = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    s_plane = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    v_plane = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    h_range = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    s_range = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    v_range = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    hsv_and = cvCreateImage( cvGetSize(original), IPL_DEPTH_8U, 1 );
    // разбиваем на отельные каналы
    cvCvtPixToPlane( original, h_plane, s_plane, v_plane, 0 );
    cvInRangeS(h_plane, cvScalar(Hmin), cvScalar(Hmax), h_range);
    cvInRangeS(s_plane, cvScalar(Smin), cvScalar(Smax), s_range);
    cvInRangeS(v_plane, cvScalar(Vmin), cvScalar(Vmax), v_range);

    cvNamedWindow("hsv and",CV_WINDOW_AUTOSIZE);
    cvMoveWindow("hsv_and", 0, 0);

    // складываем 
    cvAnd(h_range, s_range, hsv_and);
    cvAnd(hsv_and, v_range, hsv_and);
    while(true){
        cvShowImage( "hsv and", hsv_and );

        char c = cvWaitKey(33);
            if (c == ESC) { // если нажата ESC - выходим
                break;
            }
    }

    // удаляем окна
    cvDestroyAllWindows();
}

LaserAutoCalibrator::LaserAutoCalibrator(IplImage* img){
    calibrationImage = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 3 );
    // копируем изображение
    cvCopyImage(img, calibrationImage);
    getLaserAuto();
}

LaserAutoCalibrator::~LaserAutoCalibrator(){
    cvDestroyAllWindows();
}

void LaserAutoCalibrator::getParams(int* Hmini, int* Hmaxi, int* Smini, int* Smaxi, int* Vmini, int* Vmaxi) const{
    *Hmini = Hmin;
    *Hmaxi = Hmax;
    *Smini = Smin;
    *Smaxi = Smax;
    *Vmini = Vmin;
    *Vmaxi = Vmax;
}

